﻿using System;
using System.Collections.Generic;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.Base.Entity;
using T.ServiceComponent.Monitor.Client;
using T.ServiceComponent.Kernel.Service;
using T.FAS.Runtime.Base.Common;
using T.ServiceComponent.Error;
using Newtonsoft.Json;
using ExceptionHelper = T.FAS.Runtime.Base.Common.ExceptionHelper;

namespace T.FAS.Runtime.DataAccess.Common
{
    public class CDOMonitor
    {
        private const string tag_CDO = "CDOCode";
        private const string tag_OpeType = "OpeType";
        private const string context_AppName = "ProcessName";
        private const string context_Error = "Error";
        private const string context_BillID = "BillID";

        /// <summary>
        /// 监控CDO TPS
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="opType"></param>
        /// <param name="record"></param>
        public static void MonitorTPS(CompositeDataObject cdo, OpeType opType = OpeType.Create, Exception e = null, TRecord record = null)
        {
            string monitorMetaID = CDOConstFac.GetCDOMonitorConst().TPS;
            BuildCDOMonitorParam(cdo, opType, e, record, out Dictionary<string, string> tags, out Dictionary<string, string> context);
            MonitorClient.Send(monitorMetaID, 1, tags, context);
        }

        /// <summary>
        /// 监控CDO 执行时长
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="duration"></param>
        /// <param name="opType"></param>
        /// <param name="e"></param>
        /// <param name="record"></param>
        public static void MonitorDuration(CompositeDataObject cdo, double duration, OpeType opType, Exception e = null, TRecord record = null)
        {
            string monitorMetaID = CDOConstFac.GetCDOMonitorConst().Duration;
            BuildCDOMonitorParam(cdo, opType, e, record, out Dictionary<string, string> tags, out Dictionary<string, string> context);
            MonitorClient.Send(monitorMetaID, duration, tags, context);
        }

        /// <summary>
        /// CDO全链路信息
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="opType"></param>
        /// <param name="e"></param>
        /// <param name="record"></param>
        public static void MonitorTrace(CompositeDataObject cdo, DateTime start, OpeType opType = OpeType.Create, Exception e = null, TRecord record = null, string billID = null)
        {
            try
            {
                var endTime = DateTime.Now;
                var span = endTime - start;
                var requestID = RpcContext.Current.RequestID;
                var rpcID = RpcContext.Current.RpcID;
                var longValues = new Dictionary<string, long>(StringComparer.OrdinalIgnoreCase);
                var doubleValues = new Dictionary<string, double>() { { "TimeSpan", span.TotalMilliseconds } };
                var tags = new Dictionary<string, string>
                {
                    { context_AppName, ContextInfo.ApplicationName }
                };
                var context = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
                {
                    { "RpcID", rpcID },
                    { "RequestID", requestID },
                    { "TraceType", "CDO" },
                    { "HostName", FASContextHelper.GetInstance().HostName },
                    { "ProcessName", FASContextHelper.GetInstance().ProcessName },
                    { tag_CDO, $"{cdo.Code}" },
                    { tag_OpeType, $"{opType}" },
                    { "BillID", record?.GetPKValue(cdo, false) },
                    { "ServiceID", "CDOService"},
                    { "BeginTime", start.ToString("yyyy-MM-dd HH:mm:ss.fff") },
                    { "EndTime", endTime.ToString("yyyy-MM-dd HH:mm:ss.fff") }
                };

                if (e != null)
                {
                    context.Add("InvokeResult", "Error");
                    context.Add("ExceptionMessage", e.Message);
                    context.Add("ExceptionStack", e.GetFullStackTrace());
                }
                else
                {
                    context.Add("InvokeResult", "OK");
                }
                if (opType == OpeType.Create || opType == OpeType.Update)
                {
                    if (record != null)
                    {
                        try
                        {
                            var pkVal = record.GetCDOTRecordPrimaryKeyValue(cdo);
                            context[context_BillID] = pkVal;
                        }
                        catch
                        {
                        }
                    }
                }
                else
                {
                    context[context_BillID] = billID;
                }

                MonitorClient.Trace(requestID, rpcID, longValues, doubleValues, tags, context);
            }
            catch(Exception exp)
            {
                LocalLogService.WriteError(exp, $"CDO:{cdo?.Code}全链路上报失败，msg:{exp.Message}");
            }
        }

        /// <summary>
        /// 构件CDO监控参数
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="opType"></param>
        /// <param name="e"></param>
        /// <param name="record"></param>
        /// <param name="tags"></param>
        /// <param name="context"></param>
        private static void BuildCDOMonitorParam(CompositeDataObject cdo, OpeType opType, Exception e, TRecord record, out Dictionary<string, string> tags, out Dictionary<string, string> context)
        {
            tags = new Dictionary<string, string> { { tag_CDO, $"{cdo.Code}" }, { tag_OpeType, opType.ToString() } };
            context = new Dictionary<string, string> { { context_AppName, ContextInfo.ApplicationName } };
            if (e != null)
            {
                context[context_Error] = e.Message;
            }
            if (record != null)
            {
                try
                {
                    var pkVal = record.GetCDOTRecordPrimaryKeyValue(cdo);
                    context[context_BillID] = pkVal;
                }
                catch
                {
                }
            }
        }

        /// <summary>
        /// 上报根因
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="start"></param>
        /// <param name="e"></param>
        /// <param name="opType"></param>
        public static void RootCaseReport(CompositeDataObject cdo, DateTime start, Exception e, OpeType opType = OpeType.Create)
        {
            if (e == null)
                return;
            var ex = ExceptionHelper.GetLastError(e);
            if (ex == null)
                return;
            //如果是远程异常，不上报异常根因监控
            if (ExceptionHelper.IsRemoteException(e))
                return;
            var requestID = RpcContext.Current.RequestID;
            var rpcID = RpcContext.Current.RpcID;
            var endTime = DateTime.Now;
            Dictionary<string, string> tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            Dictionary<string, string> context = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            try
            {
                //Tag：程序信息
                tags.Add("HostID",      FASContextHelper.GetInstance().HostID);          //异常发生地
                tags.Add("HostType",    FASContextHelper.GetInstance().HostType);        //异常发生地
                tags.Add("ClientIP",    FASContextHelper.GetInstance().HostName);        //异常发生地
                tags.Add("ServerIP",    ExceptionHelper.GetTargetFromContext(e));        //程序访问的远程目标地址

                //Tag：服务信息
                tags.Add(tag_CDO,       cdo.Code);                  //CDO标识
                tags.Add(tag_OpeType,   opType.ToString());         //操作类型
                tags.Add("SourceServiceID", cdo.Code + ":owner_" + cdo.LastModifier);     //海哥提供的异常根因上报，获取负责人的写法，元数据标识 + 负责人

                //Tag：异常信息
                tags.Add("ErrorType",       Convert.ToString(ex.GetType()));
                tags.Add("ErrorMessage",    ex.Message);
                tags.Add("ErrorCode",       ex is TException eee ? eee.Code : "");
                tags.Add("ErrorSource",     ex.Source);
                tags.Add("ErrorLevel",      ExceptionHelper.GetErrorLevel(ex));

                //Context：全链路信息
                context.Add("RequestID", requestID);
                context.Add("RpcID", rpcID);
                context.Add("TraceType", "CDO");
                context.Add("BeginTime", start.ToString("yyyy-MM-dd HH:mm:ss.fff"));
                context.Add("EndTime", endTime.ToString("yyyy-MM-dd HH:mm:ss.fff"));
                context.Add("ProcessName", FASContextHelper.GetInstance().ProcessName);

                MonitorClient.Send(FASContextHelper.GetInstance().RootCaseMonitorMetaData, 1, tags, context);
            }
            catch
            { }
        }
    }
}